package abstractComponent;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Semaphore;

import concreteComponents.SumadorDeListas;

import ar.edu.unq.tpi.pconc.Channel;

public abstract class ComponenteAsincronica {
	Semaphore minimo = new Semaphore(0);
	Semaphore accionTerminada = new Semaphore(0);
	Semaphore mutexA = new Semaphore(1, true);

	List<Integer> argumentos = new ArrayList<Integer>();
	int cantArgumentosMinimos;
	int cantEntrada = 0;
	int resultado;

	static Map<String, Class<? extends ComponenteAsincronica>> map = new HashMap<String, Class<? extends ComponenteAsincronica>>();
	
	public class SalidaThread extends Thread{
		
		private Set<Channel<Integer>> channels;
		
		public SalidaThread(Set<Channel<Integer>> channels0) {
			this.channels = channels0;
		}
		
		@Override
		public void run() {
			while(true){
				accionTerminada.acquireUninterruptibly();
				
				mutexA.acquireUninterruptibly();
				for (Channel<Integer> channel : channels) {	
					channel.send(resultado);
				}
				mutexA.release();
			}
		}
	}
	
	public class EntradaThread extends Thread{
		
		private Channel<Integer> channel;
		
		public EntradaThread(Channel<Integer> channel0) {
			this.channel = channel0;
		}
		
		@Override
		public void run() {
			while(true){
				Integer mensaje = channel.receive();
				
				mutexA.acquireUninterruptibly();
				argumentos.add(mensaje);
				if(++cantEntrada == cantArgumentosMinimos){
					minimo.release();
				}
				mutexA.release();
			}
		}
	}
	
	public class AccionThread extends Thread{
		@Override
		public void run() {
			while(true){
				minimo.acquireUninterruptibly();
				mutexA.acquireUninterruptibly();
				resultado = accion(argumentos);
				cantEntrada = 0;
				argumentos = new ArrayList<Integer>();
				accionTerminada.release();
				mutexA.release();
			}
		}
	}

	public static void main(String[] args) throws InstantiationException,
				IllegalAccessException, InterruptedException {
		Set<Channel<Integer>> channelsS = new HashSet<Channel<Integer>>();
		Set<Channel<Integer>> channelsR = new HashSet<Channel<Integer>>();
		
		map.put("SumadorDeListas", SumadorDeListas.class);
		ComponenteAsincronica instance = map.get(args[0]).newInstance();
		instance.cantArgumentosMinimos = Integer.parseInt(args[1]);
		
		String flag="";
		for (int n = 2;n < args.length;n++) {

			if(args[n].equals("-cs")){
				flag = "cs";
				continue;
			} 

			if(args[n].equals("-cr")){
				flag = "cr";
				continue;
			}

			if(flag.equals("cs")){
				int i = Integer.parseInt(args[n]);
				channelsS.add(new Channel<Integer>(i));
			}
			else{
				int i = Integer.parseInt(args[n]);
				channelsR.add(new Channel<Integer>(i));
			}
		}
		
		instance.new AccionThread().start();
		instance.new SalidaThread(channelsS).start();
			
		for(Channel<Integer> channel : channelsR){
			instance.new EntradaThread(channel).start();
		}
	}

	public abstract int accion(List<Integer> argumentos0);

}